<?php
declare (strict_types=1);

namespace addons\BuilderPlus\contract;

use addons\BuilderPlus\exception\BuilderException;
use addons\BuilderPlus\facade\Table;
use DateTime;

trait MethodsModel
{

    public static function getTableList(array $param, array $options): array
    {
        try {

            $arguments = Table::getCache($param['builderTableToken']);

            if (empty($arguments)) {
                throw new BuilderException('页面加载失败，请尝试刷新页面后重试！');
            }

            if (!empty($param['getTableArguments'])) {
                return self::getTableArguments($arguments);
            }


            $that = parent::field('*');
            if (!empty($param['tableSort'])) {
                [$field, $order] = explode(',', $param['tableSort']);
                $withSort = self::matchWithSort($arguments['columns'], $field);

                $withSort ? $that->hasWhere($withSort, function ($query) use ($field, $order) {
                    $query->order($field, $order);
                })->orderRaw(" `{$withSort}`.{$field} {$order}") : $that->order($field, $order);
            } else {
                $that->order($arguments['sortType']);
            }
            if (!empty($options['with'])) $that->with($options['with']);

            if (!empty($param['searchOpen'])) {
                if (!empty($param['exportCheckIds']) && !empty($param['tableExport'])) {
                    $that->whereIn($options['pk'], $param['exportCheckIds']);
                } else {
                    $that = self::processSearch($that, $param, $arguments);
                }
            }

            if (!empty($arguments['specifyQuery'])) {
                $that->where($arguments['specifyQuery']);
            }

            if (!empty($arguments['specifyWithQuery'])) {
                foreach ($arguments['specifyWithQuery'] as $key => $val) {
                    $that->hasWhere($key, function ($query) use ($val) {
                        $query->where($val);
                    });
                }
            }

            if (!empty($arguments['tree'])) {
                $key = !empty($options['tree']['pid']) ? $options['tree']['pid'] : 'pid';
                $that->where($key, !empty($param['parentId']) ? $param['parentId'] : 0);
            }

            if (!empty($options['append'])) $that->append($options['append']);

            $count = 0;
            if (empty($param['tableExport'])) {
                $count = getCount($that->fetchSql()->select());
                $that->page(intval($param['page'] ?? 1), intval($param['limit'] ?? 20));
            } else {
                ini_set('memory_limit', $options['memory_limit'] ?? '1024M');
            }
            $list = $that->select();
            return ['code' => 0, 'data' => $list, 'count' => $count, 'census' => []];

        } catch (\Exception $e) {

            return ['code' => -1, 'msg' => $e->getMessage()];
        }
    }

    public static function matchWithSort(array $columns, string $field): string
    {
        foreach ($columns as $column) {
            if ($field == $column['field'] && !empty($column['withSort'])) {
                return $column['withSort'];
            }
        }
        return '';
    }

    public static function getTableArguments($arguments): array
    {
        return ['code' => 1, 'msg' => 'success', 'data' => [
            'top_buttons'   => $arguments['top_buttons'],
            'right_buttons' => $arguments['right_buttons'],
            'search_items'  => $arguments['search_items'],
            'columns'       => $arguments['columns'],
            'switch'        => $arguments['switch'],
            'fastSort'      => $arguments['fastSort'],
            'totalRow'      => $arguments['totalRow'],
            'tree'          => $arguments['tree'],
            'treeConfig'    => $arguments['treeConfig'],
            'config'        => $arguments['config'],
            'url'           => $arguments['url'],
            'token'         => $arguments['token'],
            'menu'          => $arguments['menu'],
            'pk'            => $arguments['builderPk']
        ]];
    }

    public static function processSearch($that, $param, $arguments)
    {
        try {

            foreach (array_merge($arguments['search_items'], $arguments['search_tab_items']) as $items) {
                $field = $items['field'];
                $fieldVal = '';
                if ($items['type'] != 'linkage') {
                    if (empty($param[$items['field']])) {
                        continue;
                    }
                    $fieldVal = $param[$items['field']];
                }
                $condition = $items['condition'];
                if ($items['type'] == 'checkbox') {
                    $condition = 'In';
                }

                $type = $items['type'];

                switch ($type) {
                    case "linkage":
                        $linkageField = explode('-', $field);
                        foreach ($linkageField as $item) {
                            if (!empty($param[$item])) {
                                $that->where($item, $condition, $param[$item]);
                            }
                        }
                        break;
                    case "date":
                        $field = strpos($field, "__") ? str_replace("__", "|", $field) : $field;
                        if ($items['dateRange']) {
                            [$start, $end] = explode(' - ', $fieldVal);
                            [$start, $end] = match ($items['dateType']) {
                                'year' => [$start . '-01-01', (new DateTime("$end-12-31"))
                                    ->format('Y-m-d')
                                ],
                                'month' => [$start . '-01', (new DateTime("$end-01"))
                                    ->modify('last day of this month')
                                    ->format('Y-m-d')
                                ],
                                'time' => [date('Y-m-d ') . $start, date('Y-m-d ') . $end],
                                default => [$start, $end]
                            };
                        } else {
                            $start = $fieldVal;
                            $end = $fieldVal;
                        }
                        $extra = in_array($items['dateType'], ['time', 'datetime']) ? 0 : 86399;
                        $that->whereTime($field, 'between', [strtotime($start), strtotime($end) + $extra]);
                        break;
                    default:
                        $field = strpos($field, "__") ? str_replace("__", "|", $field) : $field;
                        if ($items['relationClass'] && $items['relationField']) {
                            [$m, $f] = explode(',', $items['relationField']);
                            if (in_array($condition, ['=', '<>', '>', '>=', '<', '<='])) {
                                $hasThat = $items['relationClass']::where($m, $condition, $fieldVal);

                            } else {
                                $method = 'where' . ucfirst($condition);
                                $hasThat = match ($condition) {
                                    'Like', 'NotLike' => $items['relationClass']::{$method}($m, '%' . $fieldVal . '%'),
                                    default => $items['relationClass']::{$method}($m, $fieldVal)
                                };
                            }
                            $that->whereIn($field, $hasThat->column($f));
                            break;
                        } else {
                            if (in_array($condition, ['=', '<>', '>', '>=', '<', '<='])) {
                                $that->where($field, $condition, $fieldVal);
                            } else {
                                $method = 'where' . ucfirst($condition);
                                match ($condition) {
                                    'Like', 'NotLike' => $that->{$method}($field, '%' . $fieldVal . '%'),
                                    default => $that->{$method}($field, $fieldVal)
                                };
                            }
                        }
                }
            }

            return $that;
        } catch (\Exception $e) {
            throw new BuilderException($e->getMessage());
        }
    }

    public static function methodsUpdate($param, $options): array
    {
        parent::startTrans();
        try {
            $arguments = Table::getCache($param['builderTableToken']);
            if (empty($arguments)) {
                throw new BuilderException('加载失败，未读取到主页面数据');
            }
            if (empty($param['update'])) {
                throw new BuilderException('没有要修改的数据');
            }

            parent::update($param['update'], [$options['pk'] => $param['id']]);

            Table::trigger('logs', ['type' => 'update', 'param' => $param, 'arguments' => $arguments]);

            parent::commit();
            return ['code' => 1, 'msg' => '修改成功'];
        } catch (\Exception $e) {
            parent::rollback();
            return ['code' => -1, 'msg' => $e->getMessage()];
        }
    }


    public static function methodsDelete($param, $options): array
    {
        try {

            $arguments = Table::getCache($param['builderTableToken']);
            if (empty($arguments)) {
                throw new BuilderException('加载失败，未读取到主页面数据');
            }
            if (empty($param['id'])) {
                throw new BuilderException('未选中要删除的数据');
            }

            parent::destroy(explode(',', $param['id']));

            Table::trigger('logs', ['type' => 'delete', 'param' => $param, 'arguments' => $arguments]);

            return ['code' => 1, 'msg' => '已删除'];
        } catch (\Exception $e) {
            return ['code' => -1, 'msg' => $e->getMessage()];
        }
    }

    public static function methodsSave($arguments, $options): array
    {
        try {
            $param = $options['just'] ? $arguments['data'] : $arguments;
            if (!empty($param[$options['pk']])) {
                $original = parent::where($options['pk'], $param[$options['pk']])->find();
                if (empty($original)) {
                    throw new BuilderException('记录不存在，请检查！');
                }
                parent::update($param);
                if ($options['just']) {
                    Table::trigger('logs', ['type' => 'save', 'param' => $arguments]);
                }
            } else {
                parent::create($param);
            }
            return ['code' => 1, 'msg' => '操作成功'];
        } catch (\Exception $e) {
            return ['code' => -1, 'msg' => $e->getMessage()];
        }
    }


    /**
     * @param array $map - 查询条件，数组格式
     * @param bool $merge - 是否合并公共查询条件
     * @param string $sort - 排序
     * @param array $with - 是否关键查询
     * 原点 2021年12月22日10:21:58 获取所有数据
     */
    public static function getList($map = [], $merge = true, $sort = 'sort', $with = [], $field = '*', $append = [])
    {
        try {

            $map = $merge ? array_merge($map, ['status' => 1]) : $map;

            $that = !empty($with) ? parent::with($with)->where($map) : parent::where($map);

            return $that->order($sort, 'desc')->field($field)->append($append)->select();

        } catch (\Exception $e) {

            return [];

        }
    }

    /**
     * @param array $map - 查询条件，数组格式
     * @param string $field - 查询字段，默认*
     * @param int $page - 当前页
     * @param int $limit - 每页条数，默认20
     * @param bool $merge - 是否合并is_del，status等公共状态查询条件
     * @param array $with - 是否关键查询
     * @return array
     * 原点 2021年12月22日11:26:47 分页获取数据
     */
    public static function getPageList($map = [],
                                       $field = '*',
                                       $page = 1,
                                       $limit = 20,
                                       $merge = true,
                                       $with = [],
                                       $in = [],
                                       $sort = [],
                                       $like = [],
                                       $time = [], $withoutField = '', $append = [])
    {
        try {
            $map = $merge ? array_merge($map, ['status' => 1]) : $map;
            $that = !empty($with) ? parent::with($with)->where($map) : parent::where($map);
            if (!empty($like)) {
                $that->whereLike($like['field'], '%' . $like['keyword'] . '%');
            }
            if ($time) {
                foreach ($time as $k => $v) {
                    [$start, $end] = explode(' - ', $v['value']);
                    $that->whereBetweenTime($v['key'], $start, $end);
                }
            }
            if (!empty($in)) {
                foreach ($in as $k => $v) {
                    $that->whereIn($k, $v);
                }
            }
            if ($withoutField) {
                $that->withoutField($withoutField);
            } else {
                $that->field($field);
            }
            $count = $that->count();
            $that->page(intval($page), intval($limit));
            if (!$sort) {
                $that->order($merge ? 'sort' : 'id', 'desc');
            } else {
                if (isset($sort[0])) {
                    foreach ($sort as $sortVal) {
                        $that->order($sortVal['key'], $sortVal['value']);
                    }
                } else {
                    $that->order($sort['key'], $sort['value']);
                }
            }
            $list = $that->select();
            if ($append) {
                $list->append($append);
            }
            $allPage = intval(ceil($count / $limit));

            return ['code' => 1, 'msg' => 'success', 'data' => $list, 'pages' => $allPage];
        } catch (\Exception $e) {

            return ['code' => -1, 'message' => $e->getMessage()];

        }
    }
}